/*
通过反射修改类的属性
我们还可以通过反射访问一个类中定义的成员字段，也可以修改一个类的对象中的成员字段值，听过getField()方法来获取一个类的指定字段
 */
package Class_Object;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

public class ModifyAttributes {
    public static void main(String[] args) throws ReflectiveOperationException {
        Class<?> clazz = Class.forName("com.test.Student");  //通过反射获取类对象
        Object instance = clazz.newInstance();  //使用类对象创建Student的实例instance

        Field field = clazz.getField("i");  //使用getField()方法获取类的成员字段i，赋给field
        field.set(instance, 100);  //将类实例instance的成员变量i修改为100
        //在得到Field后，我们就可以直接通过set()方法为某个对象，设定此属性的值。比如上面，就为instance对象设定值为100

        Method method = clazz.getMethod("test5");  //使用getMethod()方法获取类的方法
        method.invoke(instance); //使用Method的invoke（调用），让实例执行method方法

        //如果遇到private字段时，同样可以按照上面的操作进行越权访问
        Field field2 = clazz.getDeclaredField("y");  //获取声明的Field，包括private的
        field2.setAccessible(true);
        field2.set(instance, 200);

        Method method2 = clazz.getMethod("test6");
        method2.invoke(instance);


        //尝试破坏正常机制
        List<String> l = new ArrayList<>();

        Field field4 = ArrayList.class.getDeclaredField("size"); //获取ArrayList的size变量，size是private的
        field4.setAccessible(true);
        field4.set(l, 10);  //修改l的size为10
        System.out.println(l.size()); //放到下面的例子后面，会被影响，size打印为300？？但怎么影响的不知道。

        l.add("测试");  //添加一个元素
        System.out.println(l.size());  //放到下面的例子后面，会被影响，查看size变为301，但怎么影响的不知道
        l.remove(10); //瞎移除都不报错
        //实际上ArrayList体系由于我们的反射操作，导致被破坏



        //现在我们已经知道，反射几乎可以把一个类的老底都拔出来，任何属性，任何内容，都可以被反射修改，无论权限修饰符是什么。
        //那如果字段是标记为又是private又是final的呢？反射也可以将final的修饰符去掉，但这个操作是封装在内部的，只能内部使用
        Integer k = 10; //Integer.java的840： private final int value。Integer包装类一旦设定了值，就是终态的，不能修改

        Field field3 = Integer.class.getDeclaredField("value");

        Field modifiersField = Field.class.getDeclaredField("modifiers"); //这里获取Field类的modifiers字段进行修改
        modifiersField.setAccessible(true);  //把modifiers()方法的private修改为可访问
        modifiersField.setInt(field3, field3.getModifiers()&~Modifier.FINAL); //移除final标记

        field3.setAccessible(true);  //把Integer的private修改下可访问
        field3.set(k, 300);

        System.out.println(k);

        //我们可以发现，发射非常暴力，就连被定义为final字段的值都能强行修改，几乎无视一切阻拦

        //再次强调，进行反射操作时，必须注意是否安全，虽然反射让我们拥有了创世主的能力，但不能滥用，要把反射当做不得已才使用的工具。


    }
}
